/**********************************************************************
 *   Copyright: (C)2024 LingYun IoT System Studio
 *      Author: LiJiahui<2199250859@qq.com>
 *
 * Description: The purpose of this code is to provide a simple C library,
 *              which providing software bit-bang of the I2C protocol on
 *              any GPIO pins for ISKBoard.
 *
 *   ChangeLog:
 *        Version    Date       Author            Description
 *        V1.0.0  2024.08.29    LiJiahui      Release initial version
 *
 ***********************************************************************/

#ifndef __W25Q_H_
#define __W25Q_H_


#include <sys/types.h>
#include <stdint.h>


/* 以下与Flash芯片通信的命令命令码由查阅W25Q256JV datasheet得来，P25 表8.1.2 */
#define SPINOR_OP_RDID			0x9f	/* Read JEDEC ID */
#define SPINOR_OP_RDSR1 		0x05    /* Read status register-1 */
#define SPINOR_OP_WREN  		0x06	/* Write enable */
#define SPINOR_OP_SE_4B			0x21	/* Sector erase (4KiB) with 4-Byte address */
#define SPINOR_OP_SE			0x20	/* Sector erase(4KiB) */
#define SPINOR_OP_CHIP_ERASE	0xc7	/* Erase whole flash chip */
#define SPINOR_OP_BE_4K_4B		0xdc 	/* Block Erase(64KiB) with 4-Byte Address  */
#define SPINOR_OP_BE_4K			0xd8	/* Block Erase(64KiB) */
#define SPINOR_OP_READ_FAST_4B	0x0c	/* Read data bytes(high frequency) */
#define SPINOR_OP_READ_FAST		0x0b	/* Read data bytes(high frequency) */
#define SPINOR_OP_PP_4B			0x12    /* Page Program (up yp 256 bytes) with 4-Byte adddress */
#define SPINOR_OP_PP			0x02	/* Page Program (up to 256 bytes) */


/* SPI 总线的硬件接口和操作函数 */
typedef struct spi_info
{
	SPI_HandleTypeDef	*hspi; /* SPI port */
	GPIO_TypeDef		*cs_gpio; /* CS GPIO group */
	uint16_t			cs_pin; /* CS GPIO pin */

	/* 函数指针（以下函数指针看 w25q.c 第 109 行宏定义） */
	void	(*select)(struct spi_info *spi); /* CS enable function(pull down CS) */
	void	(*deselect)(struct spi_info *spi); /* CS disable function(pull up CS) */
	uint8_t	(*xfer)(struct spi_info *spi, uint8_t data); /* transmit and receive one byte */
	void	(*send)(struct spi_info *spi, uint8_t *data, uint32_t bytes); /* transmit &bytes data */
	void	(*recv)(struct spi_info *spi, uint8_t *buf, uint32_t size);	/* receive &bytes data */
} spi_info_t;

/* 存储 Flash 芯片的型号参数 */
typedef struct flash_info
{
	char		*name;	/* chip name */
	uint32_t	jedec_id;	/* JEDEC ID, 3 bytes(制造商ID+存储器类型+容量编码) */
	uint32_t	capacity;	/* chip size in bytes */
	uint32_t	block_size;
	uint32_t	sector_size;
	uint32_t	page_size;
	uint32_t	n_blocks;
	uint32_t	n_sectors;
	uint32_t	n_pages;
} flash_info_t;

typedef struct spinor_info
{
	spi_info_t		*spi; /* SPI 硬件接口 */
	flash_info_t	*flash; /* Flash 芯片参数 */
	uint8_t			lock; /* 操作锁（用于线程安全） */
} spinor_info_t;

enum
{
	REG_STATUS1,
	REG_STATUS2,
	REG_STATUS3,
	REG_STATUS_MAX,
};

/*+-------------------------------+
 *|   SPI Norflash HighLevel API  |
 *+-------------------------------+*/

/* SPI Norflash API test function */
extern void spinor_test(void);

/* Initial SPI and detect the flash chip */
extern int spinor_init(struct spinor_info *spinor);

/* 该函数用于SPI NOR Flash 的整片（页）擦除，将整个 Flash 芯片的所有存储单元重置为0xFF
 * Refence:	p59 8.2.32 Chip Erase(C7h / 60h)
 */
extern int spinor_erase_chip(struct spinor_info *spinor);

/* 该函数用于SPI NOR Flash 的块擦除，按64KB块为单位擦除指定范围的数据
 * Reference:W25Q256JV datasheet P58 8.2.31 64KB Block Erase with 4-Byte Address (DCh)
 * address和size与下面的扇区擦除意义相同
 */
extern int spinor_erase_block(struct spinor_info *spinor, uint32_t address, uint32_t size);

/* 该函数用于擦除Flash存储器中以4KiB为最小单位的扇区
 * Reference:W25Q256JV datasheet P55 8.2.28 Sector Erase with 4-Byte Address (21h)
 *  address:擦除操作的起始物理地址，可以是非对齐地址，及时地址不是4KiB对齐的（如0x1001），函数内部会自动向下对其到最近的4KiB边界（即0x1000）
 *  size:要擦除的字节数，可以超过一个扇区的大小，如果size超过一个扇区，函数会擦除所有跨越的扇区，此处擦除两个扇区
 */
extern int spinor_erase_sector(struct spinor_info *spinor, uint32_t address, uint32_t size);

/* P50 8.2.23 Page Program(02h)
 * 该函数实现了页写入功能，支持非对齐地址和跨页写入
 * 限制：1、单次写入不能超过页大小（256字节）；2、写入前必须确保目标扇区已被擦除（全FF）
*/
extern int spinor_write(struct spinor_info *spinor, uint32_t address, uint8_t *data, uint32_t bytes);

/* 该函数使用 SPI Flash的快速读取指令，可读取整个芯片的任意区域
 * 快速读取模式（如命令码0x0B/0x0C）通过插入哑字节（Dummy Byte）提高时钟频率，比标准读取（如0x03）更快，适合连续大量数据读取
 * Reference  :  P40, 8.2.13 Fast Read with 4-Byte Address (0Ch)
 * address:读取操作的起始物理地址，可以是非对齐地址，及时地址不是对齐的（如0x1001），函数内部会自动向下对其到最近的边界
 * @bytes参数是读取的字节数，可超过页大小（如257字节会读取2页）
 * 快速读取支持突发模式（Burst Mode），一次命令可连续读取任意长度数据，无需多次片选（CS）切换
 */
extern int spinor_read(struct spinor_info *spinor, uint32_t address, uint8_t *buf, uint32_t bytes);


/* ----------------------------------
 *     SPI Norflash LowLevel API    /
 -----------------------------------*/

/* Detect the norflash by JEDEC ID(检测连接的SPI NOR Flash 芯片型号) */
int spinor_detect_by_jedec(struct spinor_info *spinor);

/*
 * 实现了SPI NOR Flash 芯片的 JEDEC ID 读取功能，通过SPI 总线发送标准命令（0x9F）并解析返回的 3 字节标识符
 * JEDEC ID 有三字节：高八位：厂商ID， 中间八位：设备类型 低八位：容量代码
*/
uint32_t spinor_read_jedecid(struct spi_info *spi);

/* Description:  Write Enable
 * Reference  :  P30, 8.2.1 Write Enable (06h)
 */
void spinor_write_enable(struct spi_info *spi);

/* 循环读取Flash的状态寄存器1（SR1:Flash芯片的核心控制寄存器），直到写操作（包括编程、擦除）完成
 * Reference:P14 7.1 Status Registers
 */
void spinor_WaitForWriteEnd(struct spi_info *spi);

#endif
